/**
 * @(#)Logic.java
 *
 * Copyright: Copyright (c) 2003 Carnegie Mellon University
 *
 * This program is the implementation of the original system in the Assignment 2 of "Architectures
 * of Software Systems" class. For the details of each classes, please refer to the provided javadoc
 * documents. You will also find some useful information such as implementation decisions, coding
 * standards, and naming conventions in the README file.
 */


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Writer;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Calendar;


/**
 * This class represents a logic node which is responsible for providing high-level data access
 * methods to clients using RMI mechanism. It depends on the functionalities of data node
 * which is made available also using RMI mechanism, thus acting as a "bridge" between client nodes
 * and the data node.
 *
 * @author Jung Soo Kim
 * @version 1.0
 */
public class Logic extends UnicastRemoteObject implements RILogic {

    private static final String LOGIC_NAME = "Logic";
    private static final String DATA_NAME  = "Data";
    private static final String SECU_NAME  = "Security";

    private static boolean m_UserAuthorised = false;
    private static Logger m_Logger = null;
    private static String m_UserID = "";

    /**
     * Remote reference to data node.
     */
    protected RIData rmiDataNode;
    protected RISecurity rmiSecurityNode;
    
    private boolean IsUserAuthorized( ){ return m_UserAuthorised; }

    protected File logFile;

    /**
     * Constructs a logic node. A remote reference to the data node is acquired at the time of
     * creation.
     *
     * @param sDataName the name of data node
     * @throws IOException 
     * @throws FileNotFoundException 
     */
    public Logic(String sDataName, String securityName)
           throws NotBoundException, FileNotFoundException, IOException {
        // Get the remote reference to data node.
        this.rmiDataNode = (RIData) Naming.lookup(sDataName);
        this.rmiSecurityNode = (RISecurity) Naming.lookup(securityName);
        
        m_Logger = new Logger( "LogData.log");
        
    }
    
    private class Logger
	{
    	File m_logFile;
    	String m_logInID;
    	public Logger( String logFileName )throws RemoteException, FileNotFoundException, IOException
    	{
    		this.m_logFile = new File( logFileName );
    	}
		
    	public void SetLogInID( String ID ){ this.m_logInID =ID; }
    	
		public void Log( String ID, String Command ) throws IOException
		{
			String message = ID + " " + Command;
			
			Calendar today = Calendar.getInstance();
			
			message = today.get(Calendar.HOUR) + ":" + today.get(Calendar.MINUTE) + ":" + today.get(Calendar.SECOND)+ " " + message;
			
			Writer out = new BufferedWriter( new FileWriter( this.m_logFile));
			try{
				out.write(message);
				
//				RandomAccessFile raf = new RandomAccessFile(this.m_logFile, "rw");
				
//				raf.seek(this.m_logFile.length());
				
//				raf.writeBytes(message);
			}
			finally
			{
//				raf.close();
				out.flush();
//				out.close();
			}

		} // Register

	} // logger

    /**
     * List all students.
     *
     * @return a string result of command processing
     * @throws IOException 
     */
    public String getAllStudents()
                  throws IOException {
    	
    	if ( !this.IsUserAuthorized())	return "";
        // Get all student records.
        ArrayList vStudent = this.rmiDataNode.getAllStudentRecords();

        // Construct a list of student information and return it.
        String sReturn = "";
        for (int i=0; i<vStudent.size(); i++) {
            sReturn += (i == 0 ? "" : "\n") + ((Student) vStudent.get(i)).toString();
        }
        String Command ="getAllStudents";
        this.m_Logger.Log(this.m_UserID,Command);
        return sReturn;
    }

    /**
     * List all courses.
     *
     * @return a string result of command processing
     */
    public String getAllCourses()
                  throws IOException {
    	
    	if ( !this.IsUserAuthorized())	return "";
    	
        // Get all course records.
        ArrayList vCourse = this.rmiDataNode.getAllCourseRecords();

        // Construct a list of course information and return it.
        String sReturn = "";
        for (int i=0; i<vCourse.size(); i++) {
            sReturn += (i == 0 ? "" : "\n") + ((Course) vCourse.get(i)).toString();
        }
        String Command ="getAllCourses";
        this.m_Logger.Log(this.m_UserID,Command);
        return sReturn;
    }

    /**
     * List students who registered for a course.
     *
     * @param sCID a string representing course ID
     * @param sSection a string representing course section
     * @return a string result of command processing
     */
    public String getRegisteredStudents(String sCID, String sSection)
                  throws IOException {
    	
    	if ( !this.IsUserAuthorized())	return "";
    	
        // Get the list of students who registered for the given course.
        Course objCourse = this.rmiDataNode.getCourseRecord(sCID, sSection);
        if (objCourse == null) {
            return "Invalid course ID or course section";
        }
        ArrayList vStudent = objCourse.getRegisteredStudents();

        // Construct a list of student information and return it.
        String sReturn = "";
        for (int i=0; i<vStudent.size(); i++) {
            sReturn += (i == 0 ? "" : "\n") + ((Student) vStudent.get(i)).toString();
        }
        String Command ="getRegisteredStudents";
        this.m_Logger.Log(this.m_UserID,Command);
        return sReturn;
    }

    /**
     * List courses a student has registered for.
     *
     * @param sSID a string representing student ID
     * @return a string result of command processing
     */
    public String getRegisteredCourses(String sSID)
                  throws IOException {
    	
    	if ( !this.IsUserAuthorized())	return "";
    	
        // Get the list of courses the given student has registered for.
        Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
        if (objStudent == null) {
            return "Invalid student ID";
        }
        ArrayList vCourse = objStudent.getRegisteredCourses();

        // Construct a list of course information and return it.
        String sReturn = "";
        for (int i=0; i<vCourse.size(); i++) {
            sReturn += (i == 0 ? "" : "\n") + ((Course) vCourse.get(i)).toString();
        }
        
        String Command ="getRegisteredCourses";
        this.m_Logger.Log(this.m_UserID,Command);
        return sReturn;
    }

    /**
     * List courses a student has completed.
     *
     * @param sSID a string representing student ID
     * @return a string result of command processing
     */
    public String getCompletedCourses(String sSID)
                  throws IOException {
    	
    	if ( !this.IsUserAuthorized())	return "";
    	
        // Get the list of courses the given student has completed.
        Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
        if (objStudent == null) {
            return "Invalid student ID";
        }
        ArrayList vCourseID = objStudent.getCompletedCourses();

        // Construct a list of course information and return it.
        String sReturn = "";
        for (int i=0; i<vCourseID.size(); i++) {
            String sCID = (String) vCourseID.get(i);
            String sName = this.rmiDataNode.getCourseName(sCID);
            sReturn += (i == 0 ? "" : "\n") + sCID + " " + (sName == null ? "Unknown" : sName);
        }
        
        String Command ="getCompletedCourses";
        this.m_Logger.Log(this.m_UserID,Command);
        return sReturn;
    }

    /**
     * Register a student for a course. Conflict check is done before making a registration.
     *
     * @param sSID a string representing student ID
     * @param sCID a string representing course ID
     * @param sSection a string representing course section
     * @return a string result of command processing
     */
    public String makeARegistration(String sSID, String sCID, String sSection)
                  throws IOException {
    	
    	if ( !this.IsUserAuthorized())	return "";
    	
        // Get the student and course records.
        Student objStudent = this.rmiDataNode.getStudentRecord(sSID);
        Course objCourse = this.rmiDataNode.getCourseRecord(sCID, sSection);
        if (objStudent == null) {
            return "Invalid student ID";
        }
        if (objCourse == null) {
            return "Invalid course ID or course section";
        }

        // Check if the given course conflicts with any of the courses the student has registered.
        ArrayList vCourse = objStudent.getRegisteredCourses();
        for (int i=0; i<vCourse.size(); i++) {
            if (((Course) vCourse.get(i)).conflicts(objCourse)) {
                return "Registration cconflicts";
            }
        }

        // Request validated. Proceed to register.
        this.rmiDataNode.makeARegistration(sSID, sCID, sSection);
        String Command ="makeARegistration";
        this.m_Logger.Log(this.m_UserID,Command);
        return "Successful!";
    }

    /**
     * Register a student for a course. Conflict check is done before making a registration.
     *
     * @param sSID a string representing student ID
     * @param sCID a string representing course ID
     * @param sSection a string representing course section
     * @return a string result of command processing
     */
    public boolean authorizeUser(String sSID, String sSPWD)
                  throws IOException {
        // Get the student and course records.
    	this.m_UserAuthorised = this.rmiSecurityNode.authorizeUser(sSID, sSPWD );
    	
    	if( this.m_UserAuthorised == true )
    		this.m_UserID = sSID;
    	else
    		this.m_UserID = "NA";
    	
    	String Command ="authorizeUser";
        this.m_Logger.Log(this.m_UserID,Command);        
    	return this.m_UserAuthorised;

    }    

    
 

    /**
     * Creates a logic node and starts it. Two parameters are expected, the first one as data node
     * name and the second one as logic node name.
     *
     * @param args array of input parameters
     */
    public static void main(String args[]) {
        // Check the number of parameters.
        if (args.length != 0) {
            System.out.println("Incorrect number of parameters");
            System.out.println("Usage: java Logic");
            System.exit(1);
        }

        try {
            // Create a logic node and make it available through RMI.
            Logic objLogic = new Logic(DATA_NAME, SECU_NAME);
            Naming.rebind(LOGIC_NAME, objLogic);
            System.out.println("Logic node is ready to serve data.");
            

            // Wait for user interrupt.
            System.out.println("Press Enter to terminate.");
            System.in.read();

            // Clean up and finish.
            Naming.unbind(LOGIC_NAME);
            System.out.println("Logic node is leaving, bye. Press ctrl-c if the delay is too long.");
        }
        catch (java.rmi.ConnectException e) {
            // Show error message and quit.
            System.err.println("Java RMI error: check if rmiregistry is running.");
            System.exit(1);
	}
        catch (java.rmi.NotBoundException e) {
            // Show error message and quit.
            System.err.println("Java RMI error: check if data node is running.");
            System.exit(1);
        }
        catch (Exception e) {
            // Dump the exception information for debugging.
            System.out.println("Unexpected exception at " + LOGIC_NAME);
            e.printStackTrace();
            System.exit(1);
        }
    }
}

